package com.graduation.project.service.system.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.graduation.project.entity.dto.role.SaveOrUpdateRoleDTO;
import com.graduation.project.entity.dto.role.ListRoleDTO;
import com.graduation.project.entity.dto.UpdateStatusDTO;
import com.graduation.project.entity.po.Menu;
import com.graduation.project.entity.po.Role;
import com.graduation.project.entity.po.RoleMenu;
import com.graduation.project.entity.po.UserRole;
import com.graduation.project.entity.vo.role.ListRoleVO;
import com.graduation.project.entity.vo.MenuTreeVO;
import com.graduation.project.enums.ResultCodeEnum;
import com.graduation.project.mapper.system.MenuMapper;
import com.graduation.project.mapper.system.RoleMapper;
import com.graduation.project.mapper.system.RoleMenuMapper;
import com.graduation.project.service.system.UserRoleService;
import com.graduation.project.service.system.RoleManagementService;
import com.graduation.project.service.system.RoleMenuService;
import com.graduation.project.utils.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 卑微小峰
 * @date 2022/10/29
 * 角色管理业务实现类
 */
@Service
@Slf4j
public class RoleManagementServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleManagementService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Autowired
    private RoleMenuService roleMenuService;

    @Autowired
    private UserRoleService userRoleService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultUtil<?> saveRole(SaveOrUpdateRoleDTO saveOrUpdateRoleDTO) {
        // 新增时，角色名称和编码必须唯一
        LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Role::getRolePerms, saveOrUpdateRoleDTO.getRolePerms())
                .or()
                .eq(Role::getRoleName, saveOrUpdateRoleDTO.getRoleName());
        List<Role> roles = roleMapper.selectList(lambdaQueryWrapper);
        if (ObjectUtil.isNotEmpty(roles)) {
            return ResultUtil.Failed(ResultCodeEnum.FILED.getCode(), "角色名称或编码重复");
        }
        Role newRole = new Role();
        BeanUtil.copyProperties(saveOrUpdateRoleDTO, newRole, "id");
        try {
            roleMapper.insert(newRole);
        } catch (Exception e) {
            throw new RuntimeException("新增失败");
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "新增成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultUtil<?> updateRole(SaveOrUpdateRoleDTO input) {
        if (input.getId() == null) {
            return ResultUtil.Failed(ResultCodeEnum.FILED.getCode(), "参数不能为空");
        }
        // 判断修改数据是否存在
        Role role = roleMapper.selectOne(new LambdaQueryWrapper<Role>()
                .eq(Role::getId, input.getId()));
        if (ObjectUtil.isEmpty(role)) {
            return ResultUtil.Failed(ResultCodeEnum.FILED.getCode(), "修改数据不存在");
        }
        // 判断角色名称或编码重复
        LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.ne(Role::getId, input.getId())
                .and(i -> i.eq(Role::getRoleName, input.getRoleName())
                        .or()
                        .eq(Role::getRolePerms, input.getRolePerms()));
        List<Role> roles = roleMapper.selectList(lambdaQueryWrapper);
        if (ObjectUtil.isNotEmpty(roles)) {
            return ResultUtil.Failed(ResultCodeEnum.FILED.getCode(), "角色名称或编码重复");
        }
        Role newRole = new Role();
        BeanUtil.copyProperties(input, newRole);
        try {
            roleMapper.updateById(newRole);
        } catch (Exception e) {
            throw new RuntimeException("修改失败");
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "修改成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultUtil<?> batchDeleteRoleById(List<Long> idList) {
        try {
            roleMapper.deleteBatchIds(idList);
            for (Long id : idList) {
                // 删除用户角色关系数据
                userRoleService.remove(new LambdaQueryWrapper<UserRole>().eq(UserRole::getRoleId, id));
                // 删除角色菜单关系数据
                roleMenuService.remove(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getRoleId, id));
            }
        } catch (Exception e) {
            throw new RuntimeException("删除失败");
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "删除成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultUtil<?> updateRoleStatusById(UpdateStatusDTO input) {
        // 判断修改数据是否存在
        Role role = roleMapper.selectOne(new LambdaQueryWrapper<Role>()
                .eq(Role::getId, input.getId()));
        if (ObjectUtil.isEmpty(role)) {
            return ResultUtil.Failed(ResultCodeEnum.FILED.getCode(), "修改数据不存在");
        }
        // 修改状态
        LambdaUpdateWrapper<Role> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Role::getId, input.getId())
                .set(Role::getRoleStatus, input.getStatus());
        try {
            roleMapper.update(null, updateWrapper);
        } catch (Exception e) {
            throw new RuntimeException("操作失败");
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "操作成功");
    }

    @Override
    public ResultUtil<?> listRoleByCondition(ListRoleDTO input) {
        IPage<Role> page = new Page<>();
        page.setCurrent(input.getCurrentPage());
        page.setSize(input.getPageSize());

        LambdaQueryWrapper<Role> lambdaQueryRoleWrapper = new LambdaQueryWrapper<>();
        lambdaQueryRoleWrapper.like(StringUtils.isNotBlank(input.getRoleName()), Role::getRoleName, input.getRoleName())
                .like(StringUtils.isNotBlank(input.getRolePerms()), Role::getRolePerms, input.getRolePerms())
                .eq(input.getRoleStatus() != null, Role::getRoleStatus, input.getRoleStatus());
        roleMapper.selectPage(page, lambdaQueryRoleWrapper);

        Map<String, Object>  map = new HashMap<>();
        map.put("total", page.getTotal());

        List<ListRoleVO> listRoleVo = page.getRecords().stream()
                .map(role -> {
                    ListRoleVO listRoleVO = new ListRoleVO();
                    BeanUtil.copyProperties(role, listRoleVO);
                    return listRoleVO;
                })
                .collect(Collectors.toList());

        map.put("listRoleVo", listRoleVo);

        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "查询成功", map);
    }

    @Override
    public ResultUtil<?> listMenuTree() {
        List<Menu> allMenus = menuMapper.selectList(null);

        List<MenuTreeVO> menuTreeVOList = allMenus.stream()
                // 找到所有一级菜单
                .filter(menu -> menu.getParentMenuId() == 0)
                .map(menu -> {
                    MenuTreeVO menuTreeVO = new MenuTreeVO();
                    BeanUtil.copyProperties(menu, menuTreeVO);
                    menuTreeVO.setChildren(getChildMenuTree(menuTreeVO, allMenus));
                    return menuTreeVO;
                })
                .collect(Collectors.toList());

        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "查询成功", menuTreeVOList);
    }

    @Override
    public ResultUtil<?> listMenuIdByRoleId(Long id) {
        LambdaUpdateWrapper<RoleMenu> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(RoleMenu::getRoleId, id);
        List<RoleMenu> roleMenuList = roleMenuMapper.selectList(lambdaUpdateWrapper);

        List<Long> menuIdList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(roleMenuList)) {
            menuIdList = roleMenuList.stream()
                    .map(RoleMenu::getMenuId)
                    .collect(Collectors.toList());
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "查询成功", menuIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultUtil<?> menuAssignment(Long id, List<Long> menuIds) {
        LambdaUpdateWrapper<RoleMenu> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(RoleMenu::getRoleId, id);

        List<RoleMenu> roleMenuList = new ArrayList<>();
        for (Long menuId : menuIds) {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(id);
            roleMenu.setMenuId(menuId);
            roleMenuList.add(roleMenu);
        }
        try {
            // 1、清空该角色拥有的所有权限
            roleMenuMapper.delete(lambdaUpdateWrapper);
            // 2、新增该角色权限
            roleMenuService.saveBatch(roleMenuList);
        } catch (Exception e) {
            throw new RuntimeException("操作失败");
        }
        return ResultUtil.Success(ResultCodeEnum.SUCCESS.getCode(), "操作成功");
    }

    private List<MenuTreeVO> getChildMenuTree(MenuTreeVO menuTree, List<Menu> allMenus) {
        return allMenus.stream()
                .filter(menu -> menu.getParentMenuId().equals(menuTree.getId()))
                .map(menu -> {
                    MenuTreeVO menuTreeVO = new MenuTreeVO();
                    BeanUtil.copyProperties(menu, menuTreeVO);
                    menuTreeVO.setChildren(getChildMenuTree(menuTreeVO, allMenus));
                    return menuTreeVO;
                })
                .collect(Collectors.toList());
    }
}
